package com.lhq.lingsi_study_hall.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lhq.lingsi_study_hall.common.ErrorCode;
import com.lhq.lingsi_study_hall.constant.CommonConstant;
import com.lhq.lingsi_study_hall.exception.ThrowUtils;
import com.lhq.lingsi_study_hall.manager.AIManager;
import com.lhq.lingsi_study_hall.mapper.QuestionMapper;
import com.lhq.lingsi_study_hall.model.dto.question.AIGenerateQuestionRequest;
import com.lhq.lingsi_study_hall.model.dto.question.QuestionContentDTO;
import com.lhq.lingsi_study_hall.model.dto.question.QuestionQueryRequest;
import com.lhq.lingsi_study_hall.model.entity.App;
import com.lhq.lingsi_study_hall.model.entity.Question;
import com.lhq.lingsi_study_hall.model.entity.User;
import com.lhq.lingsi_study_hall.model.enums.AppTypeEnum;
import com.lhq.lingsi_study_hall.model.vo.QuestionVO;
import com.lhq.lingsi_study_hall.model.vo.UserVO;
import com.lhq.lingsi_study_hall.service.AppService;
import com.lhq.lingsi_study_hall.service.QuestionService;
import com.lhq.lingsi_study_hall.service.UserService;
import com.lhq.lingsi_study_hall.utils.AlgorithmUtils;
import com.lhq.lingsi_study_hall.utils.JsonUtils;
import com.lhq.lingsi_study_hall.utils.SqlUtils;
import com.zhipu.oapi.service.v4.model.ModelData;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.lhq.lingsi_study_hall.constant.PromptConstant.GENERATE_QUESTION_SCORE_SYSTEM_PROMPT;
import static com.lhq.lingsi_study_hall.constant.PromptConstant.GENERATE_QUESTION_Test_SYSTEM_PROMPT;

/**
 * 题目服务实现
 *
 
 */
@Service
@Slf4j
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Resource
    private UserService userService;
    @Resource
    private AppService appService;
    @Resource
    private AIManager aiManager;

    /**
     * 校验数据
     *
     * @param question
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validQuestion(Question question, boolean add) {
        ThrowUtils.throwIf(question == null, ErrorCode.PARAMS_ERROR);
        // 从对象中取值
        String questionContent = question.getQuestionContent();
        Long appId = question.getAppId();
        // 创建数据时，参数不能为空
        if (add) {
            // 补充校验规则
            ThrowUtils.throwIf(StringUtils.isBlank(questionContent), ErrorCode.PARAMS_ERROR,"题目不能为空");
            ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR,"appId 非法");
        }
        // 修改数据时，有参数则校验
        // 补充校验规则
        if (appId != null) {
            App app = appService.getById(appId);
            ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "App不存在");
        }
    }

    /**
     * 获取查询条件
     *
     * @param questionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Question> getQueryWrapper(QuestionQueryRequest questionQueryRequest) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        if (questionQueryRequest == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = questionQueryRequest.getId();
        String questionContent = questionQueryRequest.getQuestionContent();
        Long appId = questionQueryRequest.getAppId();
        Long userId = questionQueryRequest.getUserId();
        Long notId = questionQueryRequest.getNotId();
        String sortField = questionQueryRequest.getSortField();
        String sortOrder = questionQueryRequest.getSortOrder();

        // 补充需要的查询条件
        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(questionContent), "questionContent", questionContent);

        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.ne(ObjectUtils.isNotEmpty(appId), "appId", appId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题目封装
     *
     * @param question
     * @param request
     * @return
     */
    @Override
    public QuestionVO getQuestionVO(Question question, HttpServletRequest request) {
        // 对象转封装类
        QuestionVO questionVO = QuestionVO.objToVo(question);

        // 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = question.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionVO.setUser(userVO);

        return questionVO;
    }

    /**
     * 分页获取题目封装
     *
     * @param questionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionVO> getQuestionVOPage(Page<Question> questionPage, HttpServletRequest request) {
        List<Question> questionList = questionPage.getRecords();
        Page<QuestionVO> questionVOPage = new Page<>(questionPage.getCurrent(), questionPage.getSize(), questionPage.getTotal());
        if (CollUtil.isEmpty(questionList)) {
            return questionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionVO> questionVOList = questionList.stream().map(QuestionVO::objToVo).collect(Collectors.toList());

        // 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionList.stream().map(Question::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 填充信息
        questionVOList.forEach(questionVO -> {
            Long userId = questionVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            questionVO.setUser(userService.getUserVO(user));
        });
        // endregion

        questionVOPage.setRecords(questionVOList);
        return questionVOPage;
    }

    @Override
    public List<QuestionContentDTO> aiGenerate(AIGenerateQuestionRequest aiGenerateQuestionRequest) {
        //1. 校验参数
        Long appId = aiGenerateQuestionRequest.getAppId();
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "appId 非法");
        //2. 根据id查询App
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "App不存在");
        //3. 生成题目的提示
        Integer questionNum = aiGenerateQuestionRequest.getQuestionNum();
        Integer optionNum = aiGenerateQuestionRequest.getOptionNum();
        ThrowUtils.throwIf(optionNum==null||optionNum<=0,ErrorCode.PARAMS_ERROR,"选项数量不能小于1");
        ThrowUtils.throwIf(questionNum==null||questionNum<=0,ErrorCode.PARAMS_ERROR,"题目数量不能小于1");
        //限流
        ThrowUtils.throwIf(questionNum>50,ErrorCode.PARAMS_ERROR,"一次性生成题目的数量不能大于50道");
        String userMessage = getGenerateQuestionUserMessage(app, questionNum, optionNum);
        //4. 生成题目，根据App的类型选择Prompt
        AppTypeEnum enumByValue = AppTypeEnum.getEnumByValue(app.getAppType());
        ThrowUtils.throwIf(enumByValue==null,ErrorCode.NOT_FOUND_ERROR,"不支持的App类型");
        String systemPrompt;
        if (enumByValue==AppTypeEnum.SCORE){
            systemPrompt=StringEscapeUtils.unescapeJava(GENERATE_QUESTION_SCORE_SYSTEM_PROMPT);
        }else{
            systemPrompt=StringEscapeUtils.unescapeJava(GENERATE_QUESTION_Test_SYSTEM_PROMPT);
        }
        return generateQuestionsByContext(questionNum, systemPrompt, userMessage);
    }

    @Override
    public SseEmitter aiGenerateSSE(AIGenerateQuestionRequest aiGenerateQuestionRequest) {
        //1. 校验参数
        Long appId = aiGenerateQuestionRequest.getAppId();
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "appId 非法");
        //2. 根据id查询App
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "App不存在");
        //3. 生成题目的提示
        Integer questionNum = aiGenerateQuestionRequest.getQuestionNum();
        Integer optionNum = aiGenerateQuestionRequest.getOptionNum();
        ThrowUtils.throwIf(optionNum==null||optionNum<=0,ErrorCode.PARAMS_ERROR,"选项数量不能小于1");
        ThrowUtils.throwIf(questionNum==null||questionNum<=0,ErrorCode.PARAMS_ERROR,"题目数量不能小于1");
        //限流
        ThrowUtils.throwIf(questionNum>50,ErrorCode.PARAMS_ERROR,"一次性生成题目的数量不能大于50道");
        String userMessage = getGenerateQuestionUserMessage(app, questionNum, optionNum);
        //4. 生成题目，根据App的类型选择Prompt
        AppTypeEnum enumByValue = AppTypeEnum.getEnumByValue(app.getAppType());
        ThrowUtils.throwIf(enumByValue==null,ErrorCode.NOT_FOUND_ERROR,"不支持的App类型");
        String systemPrompt;
        if (enumByValue==AppTypeEnum.SCORE){
            systemPrompt=StringEscapeUtils.unescapeJava(GENERATE_QUESTION_SCORE_SYSTEM_PROMPT);
        }else{
            systemPrompt=StringEscapeUtils.unescapeJava(GENERATE_QUESTION_Test_SYSTEM_PROMPT);
        }
        //拼接完整的字符串
        StringBuilder builder = new StringBuilder();
        //左括号计数器,除了默认值外，当回归为0时，表示左括号等于右括号数量，可以截取
        AtomicInteger accountNumber = new AtomicInteger(0);
        //5. 创建SSeEmitter对象,0L表示无限期
        SseEmitter sseEmitter = new SseEmitter(0L);
        Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(systemPrompt, userMessage);
        modelDataFlowable
                .observeOn(Schedulers.io())
                .map(modelData -> modelData.getChoices().get(0).getDelta().getContent()) // 获取文本内容
                .map(message->message.replace("\\s", "")) // 替换空格
                .filter(StringUtils::isNotBlank) // 过滤空字符串
                .flatMap(text -> {
                    List<Character> charList = new ArrayList<>();
                    for (char c : text.toCharArray()) {
                        charList.add(c);
                    }
                    return Flowable.fromIterable(charList);
                })
                .doOnNext(c -> {
                    if (c == '{') {
                        accountNumber.addAndGet(1);
                    }
                    if (accountNumber.get() >0){
                        builder.append(c);
                    }
                    if (c == '}') {
                        accountNumber.addAndGet(-1);
                        if (accountNumber.get()==0){
                            sseEmitter.send(JSONUtil.toJsonStr(builder.toString()));
                            //清空
                            builder.setLength(0);
                        }
                    }

                })
                .doOnError(error -> {
                    log.error("生成题目异常", error);
                })
                .doOnComplete(sseEmitter::complete)
                .subscribe();
        return sseEmitter;
    }

    /**
     * 通过添加对话上下文的方式来生成题目
     * @param questionNum
     * @param userMessage
     * @param systemPrompt
     * @return
     */
    @NotNull
    private List<QuestionContentDTO> generateQuestionsByContext(Integer questionNum, String systemPrompt,String userMessage) {
        String questionStr;
        if (questionNum <15){
            questionStr= aiManager.doRequest(systemPrompt, userMessage);
        }else{
            //在doRequestByContext对questionNum做了进一步的判断，但是在业务中还是尽可能的根据不同的题目数量调用不同的方法，这样相对明确。
            questionStr= aiManager.doRequestByContext(systemPrompt, userMessage, questionNum,QuestionContentDTO.class);
        }
        //截取json字符串
        questionStr=JsonUtils.captureJsonStr(questionStr);
        //去除转义字符
        questionStr= StringEscapeUtils.unescapeJava(questionStr);
        //4. 将json格式的数据转换成List<QuestionContentDTO>
        List<QuestionContentDTO> questionContentDTOList = JSONUtil.toList(questionStr, QuestionContentDTO.class);
        log.info("生成的题目数量：{}",questionContentDTOList.size());
        return questionContentDTOList;
    }

    /**
     * 通过手动过滤的方式来生成题目
     * @param questionNum
     * @param userMessage
     * @param systemPrompt
     * @return
     */
    @NotNull
    @Deprecated
    private List<QuestionContentDTO> generateQuestionsByFilter(Integer questionNum,String systemPrompt, String userMessage,
    List<QuestionContentDTO> allQuestions,Set<String> existingContent) {
        String s = aiManager.doLoopRequest(questionNum, systemPrompt, userMessage, QuestionContentDTO.class);

        //1. 截取json字符串
        s=JsonUtils.captureJsonStr(s);
        //2. 去除转义字符
        s= StringEscapeUtils.unescapeJava(s);
        //3. 将json格式的数据转换成List<QuestionContentDTO>
        List<QuestionContentDTO> questionContentDTOList = JSONUtil.toList(s, QuestionContentDTO.class);
        allQuestions.addAll(questionContentDTOList);
        //4. 计算题目的编辑距离,编辑距离小于3的题目，则认为是重复的题目，删除
        //需要重新生成的题目的数量
        int regenerateNum = 0;
        // 使用一个集合来存储已经存在的题目内容
        Iterator<QuestionContentDTO> iterator = allQuestions.iterator();
        while (iterator.hasNext()) {
            QuestionContentDTO question = iterator.next();
            for (String content : existingContent) {
                if (AlgorithmUtils.minDistance(content, question.getTitle()) < 3) {
                    iterator.remove();
                    regenerateNum++;
                    break;
                }
            }
            existingContent.add(question.getTitle());
        }
        //5. 判断是否需要重新生成题目
        if (regenerateNum > 0) {
            // 重新生成题目
            return generateQuestionsByFilter(regenerateNum, userMessage, systemPrompt, allQuestions, existingContent);
        }
        log.info("生成的题目数量：{}",allQuestions.size());
        return allQuestions;
    }

    /**
     * 获取用户生成题目的提示
     * @param app
     * @param questionNum
     * @param optionNum
     * @return
     */
    private String getGenerateQuestionUserMessage(App app, int questionNum, int optionNum) {
        return app.getAppName() + "\n" +
                app.getAppDesc() + "\n" +
                Objects.requireNonNull(AppTypeEnum.getEnumByValue(app.getAppType())).getText()  + "\n" +
                questionNum + "\n" +
                optionNum;
    }
}
